﻿#region License

// Copyright 2011 Buu Nguyen (http://www.buunguyen.net/blog)
// 
// Licensed under the Apache License, Version 2.0 (the "License"); 
// you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at 
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
// See the License for the specific language governing permissions and 
// limitations under the License.
// 
// The latest version of this file can be found at http://combres.codeplex.com

#endregion

using System.Linq;
using System.Collections.Generic;
using System.Web;
using Fasterflect;

namespace Combres
{
    /// <summary>
    /// Objects of this class are generated by <see cref="ICacheVaryProvider"/> and 
    /// used by Combres to tweak the generated URL or server-side cache key of a 
    /// <see cref="ResourceSet" />.
    /// </summary>
    public sealed class CacheVaryState
    {
        /// <summary>
        /// The key which will be used to modify the <see cref="ResourceSet"/>'s cache key
        /// and (optionally) its generated URL.
        /// </summary>
        public string Key { get; private set; }

        /// <summary>
        /// Custom data state generated by <see cref="Provider"/>.  This dictionary
        /// will be used to populate the <see cref="ICacheVaryStateReceiver.CacheVaryStates"/>
        /// of subclasses of <see cref="IContentFilter" /> and <see cref="IResourceMinifier"/> 
        /// which implement <see cref="ICacheVaryStateReceiver"/>.
        /// </summary>
        public IDictionary<string, object> Values { get; private set; }

        /// <summary>
        /// The <see cref="ICacheVaryProvider"/> object which generated this 
        /// <see cref="CacheVaryState"/> object.
        /// </summary>
        public ICacheVaryProvider Provider { get; private set; }

        /// <param name="provider"><see cref="Provider"/>.</param>
        /// <param name="key"><see cref="Key"/>.</param>
        public CacheVaryState(ICacheVaryProvider provider, string key)
            : this(provider, key, null)
        {
        }

        /// <param name="provider"><see cref="Provider"/>.</param>
        /// <param name="key"><see cref="Key"/>.</param>
        /// <param name="state">An object whose instance properties will be used to populate 
        /// <see cref="Values"/>.</param>
        public CacheVaryState(ICacheVaryProvider provider, string key, object state)
            : this(provider, key, ToDictionary(state))
        {
        }

        /// <param name="provider"><see cref="Provider"/>.</param>
        /// <param name="key"><see cref="Key"/>.</param>
        /// <param name="state"><see cref="Values"/>.</param>
        public CacheVaryState(ICacheVaryProvider provider, string key, IDictionary<string, object> state)
        {
            Provider = provider;
            Key = Encode(key);
            Values = state ?? new Dictionary<string, object>();
        }

        private static string Encode(string key)
        {
            return string.IsNullOrEmpty(key)
                       ? "_" // A random character for URL/cache segment 
                       : HttpUtility.UrlEncode(key);
        }

        private static IDictionary<string, object> ToDictionary(object state)
        {
            return state == null
                       ? null
                       : state.GetType().Properties()
                             .ToDictionary(p => p.Name, 
                                           p => p.Get(state));
        }
    }
}